﻿using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Vector2 = Microsoft.Xna.Framework.Vector2;
using Vector3 = Microsoft.Xna.Framework.Vector3;
using Matrix = Microsoft.Xna.Framework.Matrix;
using System.Collections;
using System;


namespace qtAiGamesLibrary
{
    class qtDebug
    {
        public sealed class PointBatch
        {
            private GraphicsDevice graphicsDevice;
            private List<VertexPositionColor> points =
                new List<VertexPositionColor>();
            private VertexDeclaration vertexDeclaration;
            private BasicEffect basicEffect;
            private int pointSize;

            public PointBatch(GraphicsDevice graphicsDevice, float alpha, int pointSize)
            {
                this.graphicsDevice = graphicsDevice;

                basicEffect = new BasicEffect(graphicsDevice, null);
                basicEffect.VertexColorEnabled = true;
                basicEffect.Alpha = alpha;
                basicEffect.Projection = Matrix.CreateOrthographicOffCenter(0.0F,
                    graphicsDevice.Viewport.Width, graphicsDevice.Viewport.Height, 0.0F,
                    0.0F, -1.0F);
                basicEffect.View = Matrix.Identity;
                basicEffect.World = Matrix.Identity;

                vertexDeclaration = new VertexDeclaration(graphicsDevice,
                    VertexPositionColor.VertexElements);

                this.pointSize = pointSize;
            }

            public void Begin()
            {
                points.Clear();
            }

            public void Batch(Vector2 point, Color color)
            {
                VertexPositionColor batchPoint = new VertexPositionColor(
                    new Vector3(point, 0.0F), color);

                points.Add(batchPoint);
            }

            public void End()
            {
                if (points.Count > 0)
                {
                    graphicsDevice.VertexDeclaration = vertexDeclaration;
                    graphicsDevice.RenderState.PointSize = pointSize;
                    graphicsDevice.RenderState.FillMode = FillMode.Solid;

                    basicEffect.Begin();

                    foreach (EffectPass effectPass in basicEffect.CurrentTechnique.Passes)
                    {
                        effectPass.Begin();

                        graphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                            PrimitiveType.PointList, points.ToArray(), 0, points.Count);

                        effectPass.End();
                    }

                    basicEffect.End();
                }
            }
        }

        public sealed class LineBatch
        {
            private GraphicsDevice graphicsDevice;
            private List<VertexPositionColor> points =
                new List<VertexPositionColor>();
            private List<short> indices = new List<short>();
            private VertexDeclaration vertexDeclaration;
            private BasicEffect basicEffect;

            public LineBatch(GraphicsDevice graphicsDevice, float alpha)
            {
                this.graphicsDevice = graphicsDevice;

                basicEffect = new BasicEffect(graphicsDevice, null);
                basicEffect.VertexColorEnabled = true;
                basicEffect.Alpha = alpha;
                basicEffect.Projection = Matrix.CreateOrthographicOffCenter(0.0F,
                    graphicsDevice.Viewport.Width, graphicsDevice.Viewport.Height, 0.0F,
                    0.0F, -1.0F);
                basicEffect.View = Matrix.Identity;
                basicEffect.World = Matrix.Identity;

                vertexDeclaration = new VertexDeclaration(graphicsDevice,
                    VertexPositionColor.VertexElements);
            }

            public void Begin()
            {
                points.Clear();
                indices.Clear();
            }

            public void Batch(Vector2 startPoint, Vector2 endPoint,
                Color color, float layerDepth)
            {
                Batch(startPoint, color, layerDepth);
                Batch(endPoint, color, layerDepth);
            }

            public void Batch(Vector2 startPoint, Color startColor,
                Vector2 endPoint, Color endColor, float layerDepth)
            {
                Batch(startPoint, startColor, layerDepth);
                Batch(endPoint, endColor, layerDepth);
            }

            public void Batch(Vector2 point, Color color, float layerDepth)
            {
                VertexPositionColor batchPoint =
                    new VertexPositionColor(
                    new Vector3(point, layerDepth), color);
                points.Add(batchPoint);

                indices.Add((short)indices.Count);
            }

            public void End()
            {
                if (points.Count > 0)
                {
                    graphicsDevice.VertexDeclaration = vertexDeclaration;
                    graphicsDevice.RenderState.FillMode = FillMode.Solid;

                    basicEffect.Begin();

                    foreach (EffectPass effectPass in basicEffect.CurrentTechnique.Passes)
                    {
                        effectPass.Begin();

                        graphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(
                            PrimitiveType.LineList, points.ToArray(), 0, points.Count,
                            indices.ToArray(), 0, points.Count / 2);

                        effectPass.End();
                    }

                    basicEffect.End();
                }
            }
        }

        /// <summary>
        /// A class to make primitive 2D objects out of lines.
        /// </summary>
        public class PrimitiveLine
        {
            Texture2D pixel;
            ArrayList vectors;

            /// <summary>
            /// Gets/sets the colour of the primitive line object.
            /// </summary>
            public Color Colour;

            /// <summary>
            /// Gets/sets the position of the primitive line object.
            /// </summary>
            public Vector2 Position;

            /// <summary>
            /// Gets/sets the render depth of the primitive line object (0 = front, 1 = back)
            /// </summary>
            public float Depth;

            /// <summary>
            /// Gets the number of vectors which make up the primtive line object.
            /// </summary>
            public int CountVectors
            {
                get
                {
                    return vectors.Count;
                }
            }

            /// <summary>
            /// Creates a new primitive line object.
            /// </summary>
            /// <param name="graphicsDevice">The Graphics Device object to use.</param>
            public PrimitiveLine(GraphicsDevice graphicsDevice)
            {
                // create pixels
                pixel = new Texture2D(graphicsDevice, 1, 1, 1, TextureUsage.None, SurfaceFormat.Color);
                Color[] pixels = new Color[1];
                pixels[0] = Color.White;
                pixel.SetData<Color>(pixels);

                Colour = Color.White;
                Position = new Vector2(0, 0);
                Depth = 0;

                vectors = new ArrayList();
            }

            /// <summary>
            /// Called when the primive line object is destroyed.
            /// </summary>
            ~PrimitiveLine()
            {
            }

            /// <summary>
            /// Adds a vector to the primive live object.
            /// </summary>
            /// <param name="vector">The vector to add.</param>
            public void AddVector(Vector2 vector)
            {
                vectors.Add(vector);
            }

            /// <summary>
            /// Insers a vector into the primitive line object.
            /// </summary>
            /// <param name="index">The index to insert it at.</param>
            /// <param name="vector">The vector to insert.</param>
            public void InsertVector(int index, Vector2 vector)
            {
                vectors.Insert(index, vectors);
            }

            /// <summary>
            /// Removes a vector from the primitive line object.
            /// </summary>
            /// <param name="vector">The vector to remove.</param>
            public void RemoveVector(Vector2 vector)
            {
                vectors.Remove(vector);
            }

            /// <summary>
            /// Removes a vector from the primitive line object.
            /// </summary>
            /// <param name="index">The index of the vector to remove.</param>
            public void RemoveVector(int index)
            {
                vectors.RemoveAt(index);
            }

            /// <summary>
            /// Clears all vectors from the primitive line object.
            /// </summary>
            public void ClearVectors()
            {
                vectors.Clear();
            }

            /// <summary>
            /// Renders the primtive line object.
            /// </summary>
            /// <param name="spriteBatch">The sprite batch to use to render the primitive line object.</param>
            public void Render(SpriteBatch spriteBatch)
            {
                if (vectors.Count < 2)
                    return;

                for (int i = 1; i < vectors.Count; i++)
                {
                    Vector2 vector1 = (Vector2)vectors[i - 1];
                    Vector2 vector2 = (Vector2)vectors[i];

                    // calculate the distance between the two vectors
                    float distance = Vector2.Distance(vector1, vector2);

                    // calculate the angle between the two vectors
                    float angle = (float)Math.Atan2((double)(vector2.Y - vector1.Y),
                        (double)(vector2.X - vector1.X));

                    // stretch the pixel between the two vectors
                    spriteBatch.Draw(pixel,
                        Position + vector1,
                        null,
                        Colour,
                        angle,
                        Vector2.Zero,
                        new Vector2(distance, 1),
                        SpriteEffects.None,
                        Depth);
                }
            }

            /// <summary>
            /// Creates a circle starting from 0, 0.
            /// </summary>
            /// <param name="radius">The radius (half the width) of the circle.</param>
            /// <param name="sides">The number of sides on the circle (the more the detailed).</param>
            public void CreateCircle(float radius, int sides)
            {
                vectors.Clear();

                float max = 2 * (float)Math.PI;
                float step = max / (float)sides;

                for (float theta = 0; theta < max; theta += step)
                {
                    vectors.Add(new Vector2(radius * (float)Math.Cos((double)theta),
                        radius * (float)Math.Sin((double)theta)));
                }

                // then add the first vector again so it's a complete loop
                vectors.Add(new Vector2(radius * (float)Math.Cos(0),
                        radius * (float)Math.Sin(0)));
            }
        }

    }
}


